﻿using Serilog;
using System.IO;
using System.Reflection;
using System.Runtime.Loader;
using System.Windows;

namespace Launcher
{
    // 基础启动器类
    internal abstract class BaseLauncher<T> where T : BaseLauncher<T>, new()
    {
        private static event EventHandler? Load;
        private static event EventHandler? Run;
        private static LoadingWindow? LoadingWindow { get; set; }

        protected abstract string TargetDirectory { get; }
        protected abstract string MainAssemblyName { get; }
        protected abstract string MainTypeName { get; }
        protected abstract string MainMethodName { get; }
        protected abstract string LogsSubPath { get; }
        protected abstract string ContextName { get; }

        public static void MainEntry(string[] args)
        {
            var launcher = new T();
            launcher.InitEvents();
            launcher.Execute();
        }

        protected void InitEvents()
        {
            Load += (_, _) =>
            {
                LoadingWindow = new LoadingWindow();
                LoadingWindow.Show();
            };

            Run += (_, _) =>
            {
                LoadingWindow?.Close();
                LoadingWindow = null;
            };
        }

        protected void Execute()
        {
            Load?.Invoke(null, EventArgs.Empty);

            InitLogger();

            Log.Logger.Information($"{ContextName} 应用程序启动");

            // 创建独立的加载上下文
            var alc = new AssemblyLoadContext(ContextName, isCollectible: true);

            try
            {
                // 阶段1: 在独立上下文中加载所有依赖
                Log.Logger.Information("=== 在独立上下文中加载所有程序集 ===");
                PreloadAllAssembliesToIsolatedContext(alc, TargetDirectory);

                // 阶段2: 查找并执行Main方法
                Log.Logger.Information("=== 执行主程序 ===");
                ExecuteMainMethodInIsolatedContext(alc);

                Log.Logger.Information("=== 程序执行完成 ===");
            }
            catch (Exception ex)
            {
                Log.Logger.Error(ex, "启动失败");
                MessageBox.Show("启动程序失败，请检查日志文件");
            }
            finally
            {
                // 延迟后卸载
                Log.Logger.Information("1秒后卸载上下文...");
                System.Threading.Thread.Sleep(1000);
                alc.Unload();
                Log.Logger.Information("上下文已卸载");
            }
        }

        protected void InitLogger()
        {
            Log.Logger = new LoggerConfiguration()
                .Enrich.FromLogContext()
#if RELEASE
                .MinimumLevel.Information()
#else
                .MinimumLevel.Debug()
#endif
                .WriteTo.File(
                    path: $"logs/{LogsSubPath}/log-.txt",
                    rollingInterval: RollingInterval.Day,
                    rollOnFileSizeLimit: true,
                    retainedFileCountLimit: 7,
                    outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
                    fileSizeLimitBytes: 10 * 1024 * 1024
                )
                .CreateLogger();
        }

        /// <summary>
        /// 将所有程序集加载到独立的上下文中
        /// </summary>
        protected void PreloadAllAssembliesToIsolatedContext(AssemblyLoadContext alc, string targetDirectory)
        {
            var assemblyFiles = Directory.GetFiles(targetDirectory, "*.dll");

            // 设置依赖解析（用于运行时动态加载）
            alc.Resolving += (context, assemblyName) =>
            {
                string potentialPath = Path.Combine(targetDirectory, assemblyName.Name + ".dll");
                if (File.Exists(potentialPath))
                {
                    Log.Logger.Information($"[动态解析] {assemblyName.FullName} -> {Path.GetFileName(potentialPath)}");
                    return context.LoadFromAssemblyPath(potentialPath);
                }
                Log.Logger.Warning($"[动态解析] 未找到: {assemblyName.FullName}");
                return null;
            };

            foreach (var assemblyPath in assemblyFiles)
            {
                try
                {
                    var assemblyName = AssemblyName.GetAssemblyName(assemblyPath);

                    // 检查是否已在当前ALC中加载（避免重复加载）
                    var alreadyLoaded = alc.Assemblies.Any(a =>
                        AssemblyName.ReferenceMatchesDefinition(a.GetName(), assemblyName));

                    if (!alreadyLoaded)
                    {
                        Log.Logger.Information($"加载: {assemblyName.FullName}");
                        alc.LoadFromAssemblyPath(assemblyPath);
                    }
                    else
                    {
                        Log.Logger.Information($"已加载: {assemblyName.Name}");
                    }
                }
                catch (Exception ex)
                {
                    Log.Logger.Warning($"警告: 加载 {Path.GetFileName(assemblyPath)} 失败 - {ex.Message}");
                }
            }
        }

        /// <summary>
        /// 在独立上下文中执行Main方法
        /// </summary>
        protected void ExecuteMainMethodInIsolatedContext(AssemblyLoadContext alc)
        {
            // 查找主程序集
            var mainAssembly = alc.Assemblies.FirstOrDefault(item =>
                item.GetName().Name == MainAssemblyName);

            if (mainAssembly == null)
            {
                throw new InvalidOperationException($"未找到主程序集: {MainAssemblyName}");
            }

            Log.Logger.Information($"主程序集: {mainAssembly.GetName().Name}");

            // 查找Main方法
            var mainMethod = FindMainMethod(mainAssembly);

            Log.Logger.Information($"找到Main方法: {mainMethod.DeclaringType?.FullName}.{mainMethod.Name}()");

            Run?.Invoke(null, EventArgs.Empty);

            // 执行Main方法
            mainMethod.Invoke(null, null);

            Log.Logger.Information("Main方法执行成功");
        }

        /// <summary>
        /// 查找程序集中的Main方法
        /// </summary>
        protected MethodInfo FindMainMethod(Assembly assembly)
        {
            var type = assembly.GetType(MainTypeName);
            if (type == null)
            {
                throw new InvalidOperationException($"类型 {MainTypeName} 不存在");
            }

            var mainMethod = type.GetMethod(MainMethodName,
                BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

            return mainMethod ?? throw new InvalidOperationException($"{MainTypeName}.{MainMethodName} 方法不存在");
        }
    }
}